到数组的指针

  在C++里,指针和数组密切相关。一个数组的名字能够被用做到它的开始元素的指针。例如,

    int v[] = { 1, 2, 3, 4 };
    int* p1 = v;                // 指向开始元素(隐式转换)
    int* p2 = &v[0];            // 指向开始元素
    int* p3 = &v[4];            // 指向最后元素之后一个位置

取得超出一个数组结束之后一个元素位置的指针,这件事将保证可以做到。这一点在许多算法中都非常重要(2.7.2节、18.3节)。当然,这个指针事实上并不指向数组里的一个元素,因此不能通过它去读或者写。取得数组开始元素之前的元素地址是无定义的,应该避免。在某些机器结构中,数组常常被分配在机器地址的边界上,所以“开始元素之前的一个位置”根本就没有意义。

  从数组名到这个数组的开始元素的指针的隐式转换,在C风格代码的函数调用中广泛使用。例如,

    extern "C" int strlen(const char*);        // 来自<string.h>
    void f()
    {
        char v[] = "Annemarie";
        char* p = v;            // 隐式地从char[]转换到char*
        strlen(p);
        strlen(v);              // 隐式地从char[]转换到char*
        v = p;                  // 错误❌:不能给数组赋值
    }

在两个调用中,传给标准库函数strlen()将是同一个值。困难在于无法避免这种隐式的转换。换句话说,没办法去定义一个函数,使得在函数调用时能够将整个数组复制给它。幸好并不存在从指针到数组的隐式转换。

  数组参数被隐式地转换到指针,这也意味着对于被调用函数而言,数组的大小就会被丢掉。这就要求被调函数必须以某种方式去确定数组的大小,以便完成各种有意义的操作。与C标准库中其他以字符指针为参数的函数以牙膏,strlen()依靠0确定字符串结束。strlen(p)将返回知道表示结束位置的0(但不包括它)的字符个数。这是一类相当极低的东西。标准库vector(16.3节)和string(第20章)都不会遇到这种问题。

🔚